using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace ObjectParser
{
	/// <summary>
	/// Summary description for PbViewer.
	/// </summary>
	public class PbViewer : System.Windows.Forms.PictureBox
	{
		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			// 
			// PbViewer
			// 
			this.Size = new System.Drawing.Size(232, 208);
			this.SizeChanged += new System.EventHandler(this.PbViewer_SizeChanged);
			this.Paint += new System.Windows.Forms.PaintEventHandler(this.PbViewer_Paint);
			this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.PbViewer_MouseUp);
			this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PbViewer_MouseMove);
			this.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.PbViewer_MouseWheel);
			this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.PbViewer_MouseDown);

		}
		#endregion
		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		/// <summary>
		/// The Bitmap to be drawn.
		/// </summary>
		public Bitmap Bmp;
		/// <summary>
		/// Denotes the pixel locations on the rectangle of the bitmap being represented in the image.
		/// UpdateZoom adjusts this automatically.
		/// </summary>
		public RectangleF ImgRect;
		/// <summary>
		/// The every time the image rect changes, this is incremented.
		/// </summary>
		public ulong ImgRectChangeCt=0ul;
		/// <summary>
		/// Two values that define the zooming flow location of the image (where the mouse pointer is)
		/// in Image coordinates (relative to the Bmp).
		/// </summary>
		public PointF ImgPanSource;
		/// <summary>
		/// The last size of the viewer.  Used and updated in the SizeChanged event handler.
		/// </summary>
		public Size LastViewerSize;
		/// <summary>
		/// The interpolation mode.
		/// </summary>
		public InterpolationMode InterpolationMode = InterpolationMode.Bilinear;
		/// <summary>
		/// Initializes the ImgRect to display the full Bitmap Bmp.
		/// </summary>
		public void InitZoom()
		{
			if(Bmp==null)
				InitZoom(new Rectangle(0,0,this.Width, this.Height));
			else
				InitZoom(new Rectangle(0,0,Bmp.Width, Bmp.Height));
		}
		public bool Zoom=true;
		public bool Pan=true;
		/// <summary>
		/// Initializes the image rect to encompass the rectangle.
		/// </summary>
		/// <param name="encompassed">A rectangle in image coordinates.</param>
		public void InitZoom(Rectangle encompassed)
		{
			ImgRectChangeCt++;
			//	Initialize the zoom situation.
			PointF pt = new PointF(encompassed.Left+encompassed.Width/2, encompassed.Top+encompassed.Height/2);
			float wScale = (float)this.Width/(float)encompassed.Width;
			float hScale = (float)this.Height/(float)encompassed.Height;
			float w,h;
			float whRatio = wScale/hScale;
			if (whRatio<1.0)	//	Width is the limiting reagent
			{
				w = (float)encompassed.Width/wScale;
				h = (float)encompassed.Height/hScale/whRatio;
			}
			else
			{
				w = (float)encompassed.Width/wScale*whRatio;
				h = (float)encompassed.Height/hScale;
			}
			ImgRect = new RectangleF(0.0f,0.0f,w,h);
		}
		/// <summary>
		/// Initializes the image rect to encompass the rectangle.
		/// </summary>
		/// <param name="encompassed">A rectangle in image coordinates.</param>
		public void InitZoom(RectangleF encompassed)
		{
			ImgRectChangeCt++;
			//	Initialize the zoom situation.
			PointF pt = new PointF(encompassed.Left+encompassed.Width/2, encompassed.Top+encompassed.Height/2);
			float wScale = (float)this.Width/(float)encompassed.Width;
			float hScale = (float)this.Height/(float)encompassed.Height;
			float w,h;
			float whRatio = wScale/hScale;
			if (whRatio<1.0)	//	Width is the limiting reagent
			{
				w = (float)encompassed.Width/wScale;
				h = (float)encompassed.Height/hScale/whRatio;
			}
			else
			{
				w = (float)encompassed.Width/wScale*whRatio;
				h = (float)encompassed.Height/hScale;
			}
			ImgRect = new RectangleF(0.0f,0.0f,w,h);
		}
		public float ViewerToImgX(float x)
		{
			return x * ImgRect.Width / (float)this.Width + ImgRect.Left;
		}
		public float ViewerToImgY(float y)
		{
			return y * ImgRect.Height / (float)this.Height + ImgRect.Top;
		}
		public PointF ViewerToImg(PointF pt)
		{
			return new PointF(ViewerToImgX(pt.X), ViewerToImgY(pt.Y));
		}
		public float ImgToViewerX(float x)
		{
			return (x-ImgRect.Left) * (float)this.Width / ImgRect.Width;
		}
		public float ImgToViewerY(float y)
		{
			return (y-ImgRect.Top) * (float)this.Height / ImgRect.Height;
		}
		public PointF ImgToViewer(PointF pt)
		{
			return new PointF(ImgToViewerX(pt.X), ImgToViewerY(pt.Y));
		}	
		public RectangleF ImgToViewerRectF(RectangleF rect)
		{
			float l,r,t,b;
			l = ImgToViewerX(rect.Left);
			r = ImgToViewerX(rect.Right);
			t = ImgToViewerY(rect.Top);
			b = ImgToViewerY(rect.Bottom);
			return new RectangleF(l,t,r-l,b-t);
		}
		public RectangleF ViewerToImgRect(Rectangle rect)
		{
			float l,r,t,b;
			l = ViewerToImgX((float)rect.Left);
			r = ViewerToImgX((float)rect.Right);
			t = ViewerToImgY((float)rect.Top);
			b = ViewerToImgY((float)rect.Bottom);
			return new RectangleF(l,t,r-l,b-t);
		}
		public bool CursorHidden=false;
		public void HideCursor()
		{
			if(!CursorHidden) Cursor.Hide();
			CursorHidden=true;
		}
		public void ShowCursor()
		{
			if(CursorHidden) Cursor.Show();
			CursorHidden=false;
		}
		/// <summary>
		/// Constructs a new PbViewer that will be used to display the Bitmap Bmp.
		/// </summary>
		/// <param name="Bmp"></param>
		public PbViewer()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			LastViewerSize = new Size(this.Width, this.Height);
			InitZoom();
		}
		public void Init(Bitmap Bmp)
		{
			this.Bmp = Bmp;
			InitZoom();
		}
		public virtual void PbViewer_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
		{
			if(Bmp != null)
			{
				//	Set the interpolation mode.
				e.Graphics.InterpolationMode = InterpolationMode;
				//	Get the clip rectangle in image coordinages.
				RectangleF imgRect = ViewerToImgRect( e.ClipRectangle );
				//	Draw the image.
				e.Graphics.DrawImage( Bmp, e.ClipRectangle,
					imgRect.Left, imgRect.Top, imgRect.Width, imgRect.Height, GraphicsUnit.Pixel );
			}
		}
		public virtual void PbViewer_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if(e.Button==MouseButtons.Right)		//	Drag Image
			{
				ImgPanSource.X = ViewerToImgX(e.X);
				ImgPanSource.Y = ViewerToImgY(e.Y);
				Cursor.Current = Cursors.Hand;
				ShowCursor();
			}
			else if(e.Button==MouseButtons.Middle)
			{
				this.InitZoom();
				if(this.ImgRectChanged!=null)
					ImgRectChanged(this, EventArgs.Empty);
			}
		}

		public virtual void PbViewer_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if(Zoom)
			{
				if	((  e.Delta<0.0  && Math.Min(ImgRect.Width/(float)this.Width,ImgRect.Height/(float)this.Height)<10.0
					) || (
						e.Delta>=0.0 && Math.Max(ImgRect.Width/(float)this.Width,ImgRect.Height/(float)this.Height)>0.05
					))
				{
					Point p = this.PointToClient(Cursor.Position);
					//	Compute where the cursor is in image coordinates.
					float ptx = ViewerToImgX((float)p.X);
					float pty = ViewerToImgY((float)p.Y);
					//	Compute the factor change in zoom.
					float fz = 1.0f-(float)e.Delta/120.0f/20.0f;
					//	Compute the change in left and top points.
					float dl=-(ptx-ImgRect.Left)*fz;
					float dt=-(pty-ImgRect.Top)*fz;
					//	Generate the new rectangle
					ImgRect = new RectangleF(ptx+dl, pty+dt, ImgRect.Width*fz, ImgRect.Height*fz);
					ImgRectChangeCt++;
					if(ImgRectChanged!=null)
						ImgRectChanged(this, EventArgs.Empty);
					//	Invalidate the control.
					this.Invalidate();
				}
			}
		}

		public virtual void PbViewer_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if(Pan)
			{
				if(e.Button==MouseButtons.Right)		//	Image is being panned.
				{
					float dx = ImgPanSource.X - ViewerToImgX(e.X);
					float dy = ImgPanSource.Y - ViewerToImgY(e.Y);
					ImgRect = new RectangleF(ImgRect.Left+dx, ImgRect.Top+dy, ImgRect.Width, ImgRect.Height);
					ImgRectChangeCt++;
					if(ImgRectChanged!=null)
						ImgRectChanged(this, EventArgs.Empty);
					this.Invalidate();
				}
			}
		}

		public virtual void PbViewer_SizeChanged(object sender, System.EventArgs e)
		{
			if(this.Width>0 && this.Height>0)
			{
				Size newSize = new Size(this.Width, this.Height);
				double whOld = (double)LastViewerSize.Width/(double)LastViewerSize.Height;
				double whNew = (double)newSize.Width/(double)newSize.Height;
				double newOld = whNew/whOld;
				double wNew = ImgRect.Width*Math.Sqrt(newOld);
				double hNew = ImgRect.Height/Math.Sqrt(newOld);
				float xc = ImgRect.Width/2.0f+ImgRect.Left;
				float yc = ImgRect.Height/2.0f+ImgRect.Top;
				ImgRect = new RectangleF(xc-(float)wNew/2.0f, yc-(float)hNew/2.0f, (float)wNew, (float)hNew);
				LastViewerSize = newSize;
			
				ImgRectChangeCt++;

				this.Invalidate();
			}
		}
		public virtual void Invalidate(RectangleF rf, float cushion)
		{
			Invalidate( new Rectangle( (int)(rf.Left-cushion), (int)(rf.Top-cushion),
				(int)(rf.Width+2.0f*cushion+1.0f), (int)(rf.Height+2.0f*cushion+1.0f) ) );
		}

		private void PbViewer_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			Cursor.Current = Cursors.Cross;
			ShowCursor();
		}
		public EventHandler ImgRectChanged = null;
		public new KeyEventHandler KeyDown;
		public new KeyEventHandler KeyUp;
		public new KeyPressEventHandler KeyPress;
		protected override void OnKeyDown(KeyEventArgs e)
		{
			if(KeyDown != null)
				KeyDown(this, e);
			else
				base.OnKeyDown (e);
		}
		protected override void OnKeyUp(KeyEventArgs e)
		{
			if(KeyUp != null)
				KeyUp(this, e);
			else
				base.OnKeyUp (e);
		}
		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			if(KeyPress != null)
				KeyPress(this, e);
			else
				base.OnKeyPress (e);
		}

	}
}
